<!DOCTYPE HTML>
<html lang="zh-Hans" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Rust中的生命周期——从StrSplit实例说开去 - Rust 优秀博文</title>


        <!-- Custom HTML head -->
        
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../../favicon.svg">
        <link rel="shortcut icon" href="../../favicon.png">
        <link rel="stylesheet" href="../../css/variables.css">
        <link rel="stylesheet" href="../../css/general.css">
        <link rel="stylesheet" href="../../css/chrome.css">
        <link rel="stylesheet" href="../../css/print.css" media="print">

        <!-- Fonts -->
        <link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../../fonts/fonts.css">

        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../../highlight.css">
        <link rel="stylesheet" href="../../tomorrow-night.css">
        <link rel="stylesheet" href="../../ayu-highlight.css">

        <!-- Custom theme stylesheets -->

    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">1.</strong> 类型系统</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../01_类型系统/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow.html"><strong aria-hidden="true">1.1.</strong> Rust Concept Clarification Deref vs AsRef vs Borrow vs Cow</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust_Concept_Clarification_Deref_vs_AsRef_vs_Borrow_vs_Cow/Deref_AsRef_Borrow_Cow释义.html"><strong aria-hidden="true">1.2.</strong> Deref AsRef Borrow Cow 释义</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然/Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然.html"><strong aria-hidden="true">1.3.</strong> Rust的Borrow和AsRef：让你的代码用起来像呼吸一样自然</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust的Cow类型有什么用？详解Cow及其用途/Rust的Cow类型有什么用？详解Cow及其用途.html"><strong aria-hidden="true">1.4.</strong> Rust的Cow类型有什么用？详解Cow及其用途</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/判别Fn、FnMut、FnOnce的标准/判别Fn、FnMut、FnOnce的标准.html"><strong aria-hidden="true">1.5.</strong> 判别Fn、FnMut、FnOnce的标准</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/一行代码告诉你内部可变性的真相(UnsafeCell)/一行代码告诉你内部可变性的真相(UnsafeCell).html"><strong aria-hidden="true">1.6.</strong> 一行代码告诉你内部可变性的真相(UnsafeCell)</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Tour_of_Rust's_Standard_Library_Traits/Tour_of_Rust's_Standard_Library_Traits.html"><strong aria-hidden="true">1.7.</strong> Tour of Rust's Standard Library Traits</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/逆变、协变与子类型，以及Rust/逆变、协变与子类型，以及Rust.html"><strong aria-hidden="true">1.8.</strong> 逆变、协变与子类型，以及Rust</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust自引用结构、Pin与Unpin/Rust自引用结构、Pin与Unpin.html"><strong aria-hidden="true">1.9.</strong> Rust自引用结构、Pin与Unpin</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/为什么Rust需要Pin,Unpin/为什么Rust需要Pin,Unpin.html"><strong aria-hidden="true">1.10.</strong> 译：为什么 Rust 需要 Pin, Unpin ？</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/定海神针Pin和Unpin/定海神针Pin和Unpin.html"><strong aria-hidden="true">1.11.</strong> 译：定海神针 Pin 和 Unpin</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/sizedness-in-rust/sizedness-in-rust.html"><strong aria-hidden="true">1.12.</strong> Sizedness in Rust</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/Rust生命周期集大成者PhantomData〈T〉/Rust生命周期集大成者PhantomData〈T〉.html"><strong aria-hidden="true">1.13.</strong> Rust生命周期集大成者 PhantomData&lt;T&gt;</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用Rust做类型体操_Part_0.html"><strong aria-hidden="true">1.14.</strong> 数据库表达式执行的黑魔法：用Rust做类型体操 Part 0</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：GAT实现引用类型关联_Part_1.html"><strong aria-hidden="true">1.15.</strong> 数据库表达式执行的黑魔法：GAT实现引用类型关联 Part 1</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用HRTB写bound_Part_2.html"><strong aria-hidden="true">1.16.</strong> 数据库表达式执行的黑魔法：用HRTB写bound Part 2</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：用Rust做类型体操之用宏展开重复代码_Part_3_&_4.html"><strong aria-hidden="true">1.17.</strong> 数据库表达式执行的黑魔法：用Rust做类型体操之用宏展开重复代码 Part 3 & 4</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：与Rust编译器斗智斗勇之表达式向量化_Part_5_&_6.html"><strong aria-hidden="true">1.18.</strong> 数据库表达式执行的黑魔法：与Rust编译器斗智斗勇之表达式向量化 Part 5 & 6</a></li><li class="chapter-item expanded "><a href="../../01_类型系统/数据库表达式执行的黑魔法：用Rust做类型体操/数据库表达式执行的黑魔法：在Rust中用宏关联逻辑类型和实际类型_Part_7.html"><strong aria-hidden="true">1.19.</strong> 数据库表达式执行的黑魔法：在Rust中用宏关联逻辑类型和实际类型 Part 7</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">2.</strong> 生命周期</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../02_生命周期/Rust中的生命周期——从StrSplit实例说开去/Rust中的生命周期——从StrSplit实例说开去.html" class="active"><strong aria-hidden="true">2.1.</strong> Rust中的生命周期——从StrSplit实例说开去</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/与ChatGPT深度对话来学Rust生命周期/与ChatGPT深度对话来学Rust生命周期.html"><strong aria-hidden="true">2.2.</strong> 与ChatGPT深度对话来学Rust生命周期</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——early_bound与late_bound（1）/进击的Rust生命周期——early_bound与late_bound（1）.html"><strong aria-hidden="true">2.3.</strong> 进击的Rust生命周期——early_bound与late_bound（1）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——early_bound与late_bound（2）/进击的Rust生命周期——early_bound与late_bound（2）.html"><strong aria-hidden="true">2.4.</strong> 进击的Rust生命周期——early_bound与late_bound（2）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——一力降十会的MIR（1）/进击的Rust生命周期——一力降十会的MIR（1）.html"><strong aria-hidden="true">2.5.</strong> 进击的Rust生命周期——一力降十会的MIR（1）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/进击的Rust生命周期——一力降十会的MIR（2）/进击的Rust生命周期——一力降十会的MIR（2）.html"><strong aria-hidden="true">2.6.</strong> 进击的Rust生命周期——一力降十会的MIR（2）</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/Common_Rust_Lifetime_Misconceptions/Common_Rust_Lifetime_Misconceptions.html"><strong aria-hidden="true">2.7.</strong> Common Rust Lifetime Misconceptions</a></li><li class="chapter-item expanded "><a href="../../02_生命周期/Rust生命周期常见误区/Rust生命周期常见误区.html"><strong aria-hidden="true">2.8.</strong> 译：Rust生命周期常见误区</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">3.</strong> 无畏并发</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../05_无畏并发/简单写个Rust无锁队列/简单写个Rust无锁队列.html"><strong aria-hidden="true">3.1.</strong> 简单写个Rust无锁队列</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——混合自旋锁/进击的Rust多线程——混合自旋锁.html"><strong aria-hidden="true">3.2.</strong> 进击的Rust多线程——混合自旋锁</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/An_unsafe_tour_of_Rust's_Send_and_Sync/An_unsafe_tour_of_Rust's_Send_and_Sync.html"><strong aria-hidden="true">3.3.</strong> An unsafe tour of Rust's Send and Sync</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——Send与Sync/进击的Rust多线程——Send与Sync.html"><strong aria-hidden="true">3.4.</strong> 进击的Rust多线程——Send与Sync</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/进击的Rust多线程——离经叛道的PhantomData/进击的Rust多线程——离经叛道的PhantomData.html"><strong aria-hidden="true">3.5.</strong> 进击的Rust多线程——离经叛道的PhantomData</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust_Async_Pin概念解析/Rust_Async_Pin概念解析.html"><strong aria-hidden="true">3.6.</strong> Rust Async: Pin概念解析</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust和C++的并发库对比/Rust和C++的并发库对比.html"><strong aria-hidden="true">3.7.</strong> 译：Rust 和 C++ 的并发库对比</a></li><li class="chapter-item expanded "><a href="../../05_无畏并发/Rust原子类型和内存排序/Rust原子类型和内存排序.html"><strong aria-hidden="true">3.8.</strong> Rust原子类型和内存排序</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">4.</strong> 网络编程</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../06_网络编程/从编解码层面理解WebSocket_手写一个WebSocket/从编解码层面理解WebSocket_手写一个WebSocket.html"><strong aria-hidden="true">4.1.</strong> 从编解码层面理解WebSocket 手写一 个WebSocket</a></li><li class="chapter-item expanded "><a href="../../06_网络编程/透过Rust探索系统的本原：网络篇/透过Rust探索系统的本原：网络篇.html"><strong aria-hidden="true">4.2.</strong> 透过Rust探索系统的本原：网络篇</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">5.</strong> 轮子系列</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../07_轮子系列/700行Rust写一个内存分配器/700行Rust写一个内存分配器.html"><strong aria-hidden="true">5.1.</strong> 700行Rust写一个内存分配器</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/Rust：网络库的实现思路/Rust：网络库的实现思路.html"><strong aria-hidden="true">5.2.</strong> Rust：网络库的实现思路</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/Rust异步运行时基础部件/Rust异步运行时基础部件.html"><strong aria-hidden="true">5.3.</strong> Rust异步运行时基础部件</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（1）.html"><strong aria-hidden="true">5.4.</strong> 使用Rust+epoll编写异步IO框架（1）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（2）.html"><strong aria-hidden="true">5.5.</strong> 使用Rust+epoll编写异步IO框架（2）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/使用Rust+epoll编写异步IO框架/使用Rust+epoll编写异步IO框架（3）.html"><strong aria-hidden="true">5.6.</strong> 使用Rust+epoll编写异步IO框架（3）</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day1.html"><strong aria-hidden="true">5.7.</strong> 用rust从零开发一套web框架：day1</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day2.html"><strong aria-hidden="true">5.8.</strong> 用rust从零开发一套web框架：day2</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day3.html"><strong aria-hidden="true">5.9.</strong> 用rust从零开发一套web框架：day3</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day4.html"><strong aria-hidden="true">5.10.</strong> 用rust从零开发一套web框架：day4</a></li><li class="chapter-item expanded "><a href="../../07_轮子系列/用rust从零开发一套web框架/用rust从零开发一套web框架：day5.html"><strong aria-hidden="true">5.11.</strong> 用rust从零开发一套web框架：day5</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">6.</strong> 奇技淫巧</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Copy-On-Write是不是优化？/Copy-On-Write是不是优化？.html"><strong aria-hidden="true">6.1.</strong> 译：Copy-On-Write是不是优化？</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/揭秘神奇的Rust_Axum风格的函数实现/揭秘神奇的Rust_Axum风格的函数实现.html"><strong aria-hidden="true">6.2.</strong> 译：揭秘神奇的 Rust Axum 风格的函数实现</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/“变长参数”函数与回调/“变长参数”函数与回调.html"><strong aria-hidden="true">6.3.</strong> “变长参数”函数与回调</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Rust字符串格式化的幕后：format_args!()/Rust字符串格式化的幕后：format_args!().html"><strong aria-hidden="true">6.4.</strong> 译：Rust字符串格式化的幕后：format_args!()</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/给Rust带来一点C++特产/给Rust带来一点C++特产.html"><strong aria-hidden="true">6.5.</strong> 给Rust带来一点C++特产</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/一步步实现_Rust_Bevy_ECS_的_System_简化版本/一步步实现_Rust_Bevy_ECS_的_System_简化版本.html"><strong aria-hidden="true">6.6.</strong> 一步步实现 Rust Bevy ECS 的 System 简化版本</a></li><li class="chapter-item expanded "><a href="../../08_奇技淫巧/Exploring_Design_Patterns_in_Rust_with_Algorithmic_Trading_Examples/Exploring_Design_Patterns_in_Rust_with_Algorithmic_Trading_Examples.html"><strong aria-hidden="true">6.7.</strong> Exploring Design Patterns in Rust with Algorithmic Trading Examples</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">7.</strong> 源码分析</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：bytes源码分析/Rust并发：bytes源码分析.html"><strong aria-hidden="true">7.1.</strong> Rust并发：bytes源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：标准库Arc源码分析/Rust并发：标准库Arc源码分析.html"><strong aria-hidden="true">7.2.</strong> Rust并发：标准库Arc源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust并发：标准库sync_Once源码分析/Rust并发：标准库sync_Once源码分析.html"><strong aria-hidden="true">7.3.</strong> Rust并发：标准库sync::Once源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust源码阅读：引用计数Rc/Rust源码阅读：引用计数Rc.html"><strong aria-hidden="true">7.4.</strong> Rust源码阅读：引用计数Rc</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust源码阅读：Cell、RefCell与内部可变性/Rust源码阅读：Cell、RefCell与内部可变性.html"><strong aria-hidden="true">7.5.</strong> Rust源码阅读： Cell、RefCell与内部可变性</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/关于_Rust_的_UnsafeCell、Cell_与_RefCell/关于_Rust_的_UnsafeCell、Cell_与_RefCell.html"><strong aria-hidden="true">7.6.</strong> 关于 Rust 的 UnsafeCell、Cell 与 RefCell</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust_Async_async-stream源码分析/Rust_Async_async-stream源码分析.html"><strong aria-hidden="true">7.7.</strong> Rust Async: async-stream源码分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/走进Tokio的异步世界/走进Tokio的异步世界.html"><strong aria-hidden="true">7.8.</strong> 走进 Tokio 的异步世界</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/tokio.rs_runtime的实现/tokio.rs_runtime的实现.html"><strong aria-hidden="true">7.9.</strong> tokio.rs runtime 的实现</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_internals/Tokio_internals.html"><strong aria-hidden="true">7.10.</strong> Tokio internals</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_internals/译文：Tokio內部机制.html"><strong aria-hidden="true">7.11.</strong> 译：Tokio 内部机制</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Rust_Axum_HTTP_框架的架构分析/Rust_Axum_HTTP_框架的架构分析.html"><strong aria-hidden="true">7.12.</strong> Rust Axum HTTP 框架的架构分析</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/安利一个Rust_Game_Engine：Bevy--ECS部分/安利一个Rust_Game_Engine：Bevy--ECS部分.html"><strong aria-hidden="true">7.13.</strong> 安利一个Rust Game Engine：Bevy--ECS部分</a></li><li class="chapter-item expanded "><a href="../../09_源码分析/Tokio_解析之任务调度/Tokio_解析之任务调度.html"><strong aria-hidden="true">7.14.</strong> Tokio 解析之任务调度</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">8.</strong> 生态观察</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../11_生态观察/Rust_web_frameworks_have_subpar_error_reporting/Rust_web_frameworks_have_subpar_error_reporting.html"><strong aria-hidden="true">8.1.</strong> Rust web frameworks have subpar error reporting</a></li><li class="chapter-item expanded "><a href="../../11_生态观察/SeaORM：要做Rust版本的ActiveRecord/SeaORM：要做Rust版本的ActiveRecord.html"><strong aria-hidden="true">8.2.</strong> SeaORM：要做Rust版本的ActiveRecord</a></li></ol></li><li class="chapter-item expanded "><a href="../../empty.html"><strong aria-hidden="true">9.</strong> 死灵终极</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_总述.html"><strong aria-hidden="true">9.1.</strong> 译：Learn Rust the Dangerous Way 总述</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_0.html"><strong aria-hidden="true">9.2.</strong> 译：Learn Rust the Dangerous Way 0</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_1.html"><strong aria-hidden="true">9.3.</strong> 译：Learn Rust the Dangerous Way 1</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_2.html"><strong aria-hidden="true">9.4.</strong> 译：Learn Rust the Dangerous Way 2</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_3.html"><strong aria-hidden="true">9.5.</strong> 译：Learn Rust the Dangerous Way 3</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_4.html"><strong aria-hidden="true">9.6.</strong> 译：Learn Rust the Dangerous Way 4</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Learn_Rust_the_Dangerous_Way_系列文章翻译/Learn_Rust_the_Dangerous_Way_系列文章翻译_5.html"><strong aria-hidden="true">9.7.</strong> 译：Learn Rust the Dangerous Way 5</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（一）.html"><strong aria-hidden="true">9.8.</strong> Unsafe Rust 随堂小测（一）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（二）.html"><strong aria-hidden="true">9.9.</strong> Unsafe Rust 随堂小测（二）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测（三）.html"><strong aria-hidden="true">9.10.</strong> Unsafe Rust 随堂小测（三）</a></li><li class="chapter-item expanded "><a href="../../12_死灵终极/Unsafe_Rust_随堂小测/Unsafe_Rust_随堂小测参考答案.html"><strong aria-hidden="true">9.11.</strong> Unsafe Rust 随堂小测参考答案</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Rust 优秀博文</h1>

                    <div class="right-buttons">
                        <a href="../../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>

                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>

                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="rust-中的生命周期--从-strsplit-实例说开去"><a class="header" href="#rust-中的生命周期--从-strsplit-实例说开去">Rust 中的生命周期 —— 从 StrSplit 实例说开去</a></h1>
<p>作者：<a href="https://www.zhihu.com/people/qiao-ta">偷得浮生半日闲</a></p>
<p>原载：<a href="https://zhuanlan.zhihu.com/p/442574694">https://zhuanlan.zhihu.com/p/442574694</a></p>
<p>个人网站：https://qiaoin.github.io/</p>
<p>在本文中，我们将围绕着字符串分割的实例，讲解 Rust 中的生命周期。首先我们会剖析为什么需要生命周期、什么是生命周期、以及如何标注生命周期；接下来引入多生命周期标注，并阐述什么时候需要标注多个生命周期。在此基础上，我们向前多迈一步，使用自定义的 trait 来取代分隔符的定义，让实现更加通用。最后通过查看标准库字符串分割的实现，综合理解本文中所有的知识点。</p>
<h2 id="前置要求"><a class="header" href="#前置要求">前置要求</a></h2>
<p>至少看过 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/stable/book/">Rust The Book</a> 前 8 章的内容。推荐的学习资料：</p>
<ul>
<li><a href="https://link.zhihu.com/?target=https%3A//docs.microsoft.com/en-us/learn/paths/rust-first-steps/">Take your first steps with Rust</a> 微软推出的 Rust 培训课程，可以配合视频一起使用 <a href="https://link.zhihu.com/?target=https%3A//www.youtube.com/playlist%3Flist%3DPLlrxD0HtieHjbTjrchBwOVks_sr8EVW1x">Rust for Beginners</a></li>
<li><a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/stable/book/">Rust The Book</a> —— 第 4 章和第 10 章的内容与本文密切相关，建议重新阅读一遍</li>
<li>极客时间专栏 <a href="https://link.zhihu.com/?target=https%3A//time.geekbang.org/column/intro/100085301">陈天 · Rust 编程第一课</a> —— 第 7 讲 - 第 11 讲</li>
<li><a href="https://link.zhihu.com/?target=https%3A//www.youtube.com/channel/UC_iD0xppBwwsrM9DegC5cQQ">Jon Gjengset</a> 的 YouTube 频道，本文就是 Crust of Rust 系列 <a href="https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DrAl-9HwD858%26list%3DPLqbS7AVVErFiWDOAVrPt7aYmnuuOLYvOa%26index%3D1%26ab_channel%3DJonGjengset">Lifetime Annotations</a> 的学习笔记</li>
</ul>
<h2 id="快速开始"><a class="header" href="#快速开始">快速开始</a></h2>
<p>确定目标，实现字符串分割</p>
<blockquote>
<p>input: &quot;a b c d e&quot; -- &amp;str
output: &quot;a&quot; &quot;b&quot; &quot;c&quot; &quot;d&quot; &quot;e&quot; -- 分隔符指定为 &quot; &quot;，每次 next 得到一个 &amp;str</p>
</blockquote>
<p>开始一个 Rust 项目：</p>
<pre><code class="language-bash">cargo new --lib strsplit
</code></pre>
<p>同时，我们可以使用 <a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/">Rust Playground</a> 进行练习，文中展示的所有代码都提供了 playground 链接，可以点击跳转过去，Run 起来测试一下。</p>
<h3 id="搭建骨架"><a class="header" href="#搭建骨架">搭建骨架</a></h3>
<p>定义数据结构和方法，添加单元测试，搭建好骨架：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit {
    remainder: &amp;str,
    delimiter: &amp;str,
}

impl StrSplit {
    pub fn new(haystack: &amp;str, delimiter: &amp;str) -&gt; Self {
        // ....
    }
}

impl Iterator for StrSplit {
    type Item = &amp;str;

    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
        // ...
    }
}

#[test]
fn it_works() {
    let haystack = &quot;a b c d e&quot;;
    let letters: Vec&lt;_&gt; = StrSplit::new(haystack, &quot; &quot;).collect();
    assert_eq!(letters, vec![&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;]);
}
<span class="boring">}
</span></code></pre></pre>
<p>实现 Iterator Trait 之后，就可以使用 <code>for</code> 循环遍历对应的 struct。</p>
<h3 id="为什么使用-str而不是-string"><a class="header" href="#为什么使用-str而不是-string">为什么使用 &amp;str，而不是 String？</a></h3>
<blockquote>
<p>当我们对一个知识点不熟悉时，打开 playground，写一段代码测试一下</p>
</blockquote>
<p>为了方便解释，我们写一段简单的代码（代码 0，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3D8367d7755ed94325b737bed3df5881d6">String, str and &amp;str</a>）</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    let noodles: &amp;'static str = &quot;noodles&quot;;

    // let poodles: String = String::from(noodles);
    // https://doc.rust-lang.org/std/primitive.slice.html#method.to_vec
    let poodles: String = noodles.to_string();  // 底层调用的就是 String::from(noodles);

    let oodles: &amp;str = &amp;poodles[1..];

    println!(&quot;addr of {:?}: {:p}&quot;, &quot;noodles&quot;, &amp;&quot;noodles&quot;);
    println!(&quot;addr of noodles: {:p}, len: {}, size: {}&quot;, &amp;noodles,
        noodles.len(), std::mem::size_of_val(&amp;noodles));
    println!(&quot;addr of poodles: {:p}, len: {}, capacity: {}, size: {}&quot;, &amp;poodles,
        poodles.len(), poodles.capacity(), std::mem::size_of_val(&amp;poodles));
    println!(&quot;addr of oodles: {:p}, len: {}, size: {}&quot;, &amp;oodles,
        oodles.len(), std::mem::size_of_val(&amp;oodles));
}
</code></pre></pre>
<p><code>&quot;noodles&quot;</code> 作为字符串常量（string literal），编译时存入可执行文件的 .RODATA 段，在程序加载时，获得一个固定的内存地址。作为一个字符串切片赋值给栈上变量 <code>noodles</code>，拥有静态生命周期（static lifetime），在程序运行期间一直有效。</p>
<p>当执行 <code>noodles.to_string()</code> 时，跟踪标准库实现，最后调用 <code>[u8]::to_vec_in()</code> ，在堆上分配一块新的内存，将 <code>&quot;noodles&quot;</code> 逐字节拷贝过去。</p>
<p>当把堆上的数据赋值给 <code>poodles</code> 时，<code>poodles</code> 作为分配在栈上的一个变量，其拥有（owns）堆上数据的所有权，使用胖指针（<a href="https://link.zhihu.com/?target=https%3A//stackoverflow.com/questions/57754901/what-is-a-fat-pointer">fat pointer</a>）进行<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/string/struct.String.html%23representation">表示</a>：<code>ptr</code> 指向字符串堆内存的首地址、<code>length</code> 表示字符串当前长度、<code>capacity</code> 表示分配的堆内存总容量。</p>
<p><code>oodles</code> 为字符串切片，表示对字符串某一部分（包含全部字符串）的引用的（A string slice is a reference to part of a String），包含<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/primitive.str.html%23representation">两部分内容</a>：<code>ptr</code> 指向字符串切片首地址（可以为堆内存和 static 静态内存）、<code>length</code> 表示切片长度。</p>
<p>下图清晰展示了这里的关系：</p>
<p><img src="static%5Cpict%5CRust%E4%B8%AD%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E2%80%94%E2%80%94%E4%BB%8EStrSplit%E5%AE%9E%E4%BE%8B%E8%AF%B4%E5%BC%80%E5%8E%BB%5C001.webp" alt="" /></p>
<ul>
<li><code>str</code> —— <code>[T]</code>，表示为一串字符序列（a sequence of characters），编译期无法确定其长度（dynamically sized）；</li>
<li><code>&amp;str</code> —— <code>&amp;[T]</code>，表示为一个胖指针（fat pointer），<code>ptr</code> 指向切片首地址、<code>length</code> 表示切片长度，编译期可以确定其长度为 16 字节；</li>
<li><code>String</code> —— <code>Vec&lt;T&gt;</code>，表示为一个胖指针（fat pointer），<code>ptr</code> 指向字符串堆内存的首地址、<code>length</code> 表示字符串当前长度、<code>capacity</code> 表示分配的堆内存的总容量。堆内存支持动态扩展和收缩。编译期可以确定其长度为 24 字节。</li>
</ul>
<p>在这里，针对分隔符 <code>delimiter</code>，使用 String 会存在两个问题：</p>
<p>1、涉及堆内存分配，开销大；</p>
<p>2、需要进行堆内存分配，而在嵌入式系统中是没有堆内存的，会有兼容性问题。</p>
<p>因此使用 <code>&amp;str</code> 类型。</p>
<h3 id="iterator-trait"><a class="header" href="#iterator-trait">Iterator trait</a></h3>
<p>查看标准文档 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/iter/trait.Iterator.html">Iterator trait</a></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub trait Iterator {
    /// The type of the elements being iterated over.
    type Item;

    // 必须实现的关联方法，被其他关联方法的缺省实现所依赖
    /// Advances the iterator and returns the next value.
    ///
    /// Returns [`None`] when iteration is finished. Individual iterator
    /// implementations may choose to resume iteration, and so calling `next()`
    /// again may or may not eventually start returning [`Some(Item)`] again at some
    /// point.
    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt;;

    // 其他的一些关联方法，有缺省实现
    fn collect&lt;B&gt;(self) -&gt; B
    where
        B: FromIterator&lt;Self::Item&gt;,
    { ... }

    // ...
}
<span class="boring">}
</span></code></pre></pre>
<ul>
<li>关联类型（associated types）—— 例如 <code>type Item;</code> 为迭代遍历的类型，只有在用户实现 Iterator trait 时才能够确定遍历的值的类型，延迟绑定。</li>
<li>方法（methods），也称为关联函数（associated functions）—— 对于 Iterator trait，<code>next()</code> 是必须实现的（<strong>Request methods</strong>），在值存在时，返回 <code>Some(item)</code>；值不存在时，返回 <code>None</code>。trait 中的其他方法有缺省的实现。也就是说，只要用户实现了 Iterator trait 的 <code>next()</code> 方法，该 trait 中的其他方法就有了默认实现，可以直接进行使用。</li>
</ul>
<h3 id="什么时候用-self什么时候用-self"><a class="header" href="#什么时候用-self什么时候用-self">什么时候用 Self，什么时候用 self？</a></h3>
<ul>
<li><code>Self</code> 代表当前的类型，比如 <code>StrSplit</code> 类型实现 <code>Iterator</code>，那么实现过程中使用到的 <code>Self</code> 就指代 <code>StrSplit</code>；</li>
<li><code>self</code> 在用作方法的第一个参数时，实际上就是 <code>self: Self</code>（参数名: 参数类型）的简写，所以 <code>&amp;self</code> 是 <code>self: &amp;Self</code>，而 <code>&amp;mut self</code> 是 <code>self: &amp;mut Self</code>。</li>
</ul>
<p>因此 Iterator trait 的 <code>next()</code> 签名展开为：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub trait Iterator {
    type Item;

    // fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt;;
    fn next(self: &amp;mut Self) -&gt; Option&lt;Self::Item&gt;;
}
<span class="boring">}
</span></code></pre></pre>
<h2 id="version-1-hands-on"><a class="header" href="#version-1-hands-on">version #1: hands on</a></h2>
<p>让我们直接开始吧（代码 1，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3D0ef8d7450128be332ef3861486f1eb0b">version #1: hands on</a>）</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit {
    remainder: &amp;str,
    delimiter: &amp;str,
}

impl StrSplit {
    pub fn new(haystack: &amp;str, delimiter: &amp;str) -&gt; Self {
        Self {
            remainder: haystack,
            delimiter,
        }
    }
}

impl Iterator for StrSplit {
    type Item = &amp;str;

    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
        if let Some(next_delim) = self.remainder.find(self.delimiter) {
            let until_delimiter = &amp;self.remainder[..next_delim];
            self.remainder = &amp;self.remainder[(next_delim + self.delimiter.len())..];
            Some(until_delimiter)
        } else if self.remainder.is_empty() {
            None
        } else {
            let rest = self.remainder;
            self.remainder = &quot;&quot;;
            Some(rest)
        }
    }
}

#[test]
fn it_works() {
    let haystack = &quot;a b c d e&quot;;
    let letters: Vec&lt;_&gt; = StrSplit::new(haystack, &quot; &quot;).collect();
    assert_eq!(letters, vec![&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;]);
}
<span class="boring">}
</span></code></pre></pre>
<p><code>next()</code> 的实现很简单：</p>
<p>1、在字符串中查找分隔符第一次出现的位置，如果找到返回索引值 <code>Some(usize)</code>，未找到返回 <code>None</code>；</p>
<p>2、根据索引值将字符串分为三个部分，第一部分为 <code>next()</code> 的返回值，第二部分为分隔符，第三部分为剩余待处理的字符串，在下一次调用 <code>next()</code> 作为原始字符串。</p>
<p>执行编译，报错信息如下：</p>
<pre><code class="language-bash">Compiling playground v0.0.1 (/playground)
error[E0106]: missing lifetime specifier
 --&gt; src/lib.rs:2:16
  |
2 |     remainder: &amp;str,
  |                ^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
1 ~ pub struct StrSplit&lt;'a&gt; {
2 ~     remainder: &amp;'a str,
  |

error[E0106]: missing lifetime specifier
 --&gt; src/lib.rs:3:16
  |
3 |     delimiter: &amp;str,
  |                ^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
1 ~ pub struct StrSplit&lt;'a&gt; {
2 |     remainder: &amp;str,
3 ~     delimiter: &amp;'a str,
  |

error[E0106]: missing lifetime specifier
  --&gt; src/lib.rs:16:17
   |
16 |     type Item = &amp;str;
   |                 ^ expected named lifetime parameter
   |
help: consider introducing a named lifetime parameter
   |
16 |     type Item&lt;'a&gt; = &amp;'a str;
   |              ++++    ++

For more information about this error, try `rustc --explain E0106`.
</code></pre>
<p>三个错误信息都提示缺少生命周期标注（lifetime specifier），编译器建议添加生命周期参数（lifetime parameter），因此我们在 version #1 上添加生命周期标注。</p>
<blockquote>
<p>根据错误代码 E0106 使用 <code>rustc --explain E0106</code> 探索更详细的信息，可以在浏览器中搜索 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/error-index.html%23E0106">Rust E0106</a>，也可以直接在命令行中查看，使用 playground 运行可以直接点击 [E0106] 跳转到错误说明。
E0106 错误可以分为两大类：
1、数据结构缺少生命周期标注（a lifetime is missing from a type）—— 使用数据结构时，数据结构自身的生命周期，需要小于等于其内部字段的所有引用的生命周期；
2、函数签名缺少生命周期标注，即使编译器执行生命周期自动标注，也无能为力（If it is an error inside a function signature, the problem may be with failing to adhere to the lifetime elision rules）。
编译器会通过一些简单的 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/book/ch10-03-lifetime-syntax.html%23lifetime-elision">规则</a>，自动添加生命周期标注：
1、所有引用类型的参数都有独立的生命周期 <code>'a</code>、<code>'b</code> 等（a reference gets its own lifetime parameter）；
2、如果只有一个引用型输入，它的生命周期会赋给所有输出（if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters）；
3、如果有多个引用类型的参数，其中一个是 <code>self</code>（作为数据结构的方法，第一个参数是 <code>&amp;self</code> 或 <code>&amp;mut self</code>），那么它的生命周期会赋给所有输出（if there are multiple input lifetime parameters, but one of them is <code>&amp;self</code> or <code>&amp;mut self</code> because this is a method, the lifetime of <code>self</code> is assigned to all output lifetime parameters）。</p>
</blockquote>
<h2 id="version-2-add-lifetime-specifier"><a class="header" href="#version-2-add-lifetime-specifier">version #2: add lifetime specifier</a></h2>
<p>在 playground 中多次编译，根据编译器给到的错误信息补充生命周期标注，直至编译成功（代码 2，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3Dd8a19f669b68a566912160e0e69ca7f4">version #2: add lifetime specifier</a>）</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'a&gt; {
    remainder: &amp;'a str,
    delimiter: &amp;'a str,
}

impl&lt;'a&gt; StrSplit&lt;'a&gt; {
    pub fn new(haystack: &amp;'a str, delimiter: &amp;'a str) -&gt; Self {
        Self {
            remainder: haystack,
            delimiter,
        }
    }
}

impl&lt;'a&gt; Iterator for StrSplit&lt;'a&gt; {
    type Item = &amp;'a str;

    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
        if let Some(next_delim) = self.remainder.find(self.delimiter) {
            let until_delimiter = &amp;self.remainder[..next_delim];
            self.remainder = &amp;self.remainder[(next_delim + self.delimiter.len())..];
            Some(until_delimiter)
        } else if self.remainder.is_empty() {
            None
        } else {
            let rest = self.remainder;
            self.remainder = &quot;&quot;; // -- caution
            // &amp;'a str  -----   &amp;'static str
            Some(rest)
        }
    }
}

#[test]
fn it_works() {
    let haystack = &quot;a b c d e&quot;;
    let letters: Vec&lt;_&gt; = StrSplit::new(haystack, &quot; &quot;).collect();
    assert_eq!(letters, vec![&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;]);
}
<span class="boring">}
</span></code></pre></pre>
<h3 id="数据结构的生命周期标注"><a class="header" href="#数据结构的生命周期标注">数据结构的生命周期标注</a></h3>
<p>当 struct 包含有引用类型的参数时，需要在 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/book/ch10-03-lifetime-syntax.html%23lifetime-annotations-in-struct-definitions">struct 定义时添加生命周期标注</a> —— 与声明泛型数据类型（generic data types）的语法一致，在 struct 名称后的尖括号内声明泛型生命周期参数（generic lifetime parameter），这样在 struct 的定义中就可以使用这个生命周期参数进行生命周期标注。例如 <code>remainder</code> 和 <code>delimiter</code> 是两个字符串引用，<code>StrSplit</code> 的生命周期不能大于它们，否则会访问失效的内存，因此需要进行生命周期标注。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'a&gt; {
    remainder: &amp;'a str,
    delimiter: &amp;'a str,
}
<span class="boring">}
</span></code></pre></pre>
<p>使用数据结构时，数据结构自身的生命周期，需要小于等于其内部字段的所有引用的生命周期。</p>
<p>在实现对应的数据结构时，由于 impl block 和 struct 生命周期参数是分隔开的，因此需要为 impl block 添加上生命周期参数（<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/stable/error-index.html%23E0261">E0261</a>），例如：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'a&gt; {
    remainder: &amp;'a str,
    delimiter: &amp;'a str,
}

// error[E0261]: use of undeclared lifetime name `'a`
impl StrSplit&lt;'a&gt; {
    pub fn new(haystack: &amp;'a str, delimiter: &amp;'a str) -&gt; Self {
        Self {
            remainder: haystack,
            delimiter,
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>为 impl block 添加上生命周期参数即可修复：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'a&gt; {
    remainder: &amp;'a str,
    delimiter: &amp;'a str,
}

// correct
impl&lt;'a&gt; StrSplit&lt;'a&gt; {
    pub fn new(haystack: &amp;'a str, delimiter: &amp;'a str) -&gt; Self {
        Self {
            remainder: haystack,
            delimiter,
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>同理，也适用于 <code>impl&lt;'a&gt; Iterator for StrSplit&lt;'a&gt;</code>。</p>
<h3 id="函数签名的生命周期标注"><a class="header" href="#函数签名的生命周期标注">函数签名的生命周期标注</a></h3>
<p>使用 <code>new()</code> 作为例子</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'a&gt; {
    remainder: &amp;'a str,
    delimiter: &amp;'a str,
}

impl&lt;'a&gt; StrSplit&lt;'a&gt; {
    // 去掉入参的生命周期标注
    pub fn new(haystack: &amp;str, delimiter: &amp;str) -&gt; Self {
        Self {
            remainder: haystack,
            delimiter,
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>将 <code>Self</code> 简写展开：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'a&gt; {
    remainder: &amp;'a str,
    delimiter: &amp;'a str,
}

impl&lt;'a&gt; StrSplit&lt;'a&gt; {
    pub fn new(haystack: &amp;str, delimiter: &amp;str) -&gt; StrSplit&lt;'a&gt; {
        StrSplit {
            remainder: haystack,
            delimiter,
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>函数返回值的生命周期为 <code>'a</code>，而两个入参的生命周期与 <code>'a</code> 的关系却未可知，可能在后续使用 <code>StrSplit struct</code> 时包含的两个字段 <code>remainder</code> / <code>delimiter</code>已经被释放，出现 use after free。因此需要使用生命周期参数约束入参与入参之间、入参与返回值之间的关系。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl&lt;'a&gt; StrSplit&lt;'a&gt; {
    pub fn new(haystack: &amp;'a str, delimiter: &amp;'a str) -&gt; StrSplit&lt;'a&gt; {
        StrSplit {
            remainder: haystack,
            delimiter,
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<h3 id="static-lifetime"><a class="header" href="#static-lifetime">Static lifetime</a></h3>
<p><code>next()</code> 实现的 <code>else</code> block 中执行了一个赋值操作：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>self.remainder = &quot;&quot;;
<span class="boring">}
</span></code></pre></pre>
<p>等号左侧为 <code>&amp;'a str</code>，等号右侧 <code>&quot;&quot;</code> 为字符串字面量 —— 上文我们讲到，字符串字面量拥有静态生命周期（static lifetime）—— 使用 <code>&amp;'static str</code> 表示。将 <code>&amp;'static str</code> 赋值给 <code>&amp;'a str</code>，长生命周期的值赋值给短的生命周期（subtyping system）。</p>
<h3 id="增加一个以分隔符结尾的单元测试"><a class="header" href="#增加一个以分隔符结尾的单元测试">增加一个以分隔符结尾的单元测试</a></h3>
<p>增加一个单元测试，以分隔符结尾（代码 3，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3Dc0d8920958af15ccb571d6d562ef59c8">分隔符结尾的测试报错</a>），测试报错。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[test]
fn tail_test() {
    let haystack = &quot;a b c d &quot;;
    let letters: Vec&lt;_&gt; = StrSplit::new(haystack, &quot; &quot;).collect();
    assert_eq!(letters, vec![&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;&quot;]);
}
<span class="boring">}
</span></code></pre></pre>
<p>为什么会报错呢？</p>
<p>在代码 3（代码 3，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3Dc0d8920958af15ccb571d6d562ef59c8">分隔符结尾的测试报错</a>）中，增加了 3 处 print 打印，同时修改第一个测试使之报错。</p>
<pre><code class="language-bash">---- it_works stdout ----
1-remainder = &quot;a b c d e f&quot;
1-remainder = &quot;b c d e f&quot;
1-remainder = &quot;c d e f&quot;
1-remainder = &quot;d e f&quot;
1-remainder = &quot;e f&quot;
3-remainder = &quot;f&quot;
2-remainder = &quot;&quot;

---- tail_test stdout ----
1-remainder = &quot;a b c d &quot;
1-remainder = &quot;b c d &quot;
1-remainder = &quot;c d &quot;
1-remainder = &quot;d &quot;
2-remainder = &quot;&quot;
</code></pre>
<p>观察 print 打印的信息，可以发现两个测试用例都在 <code>self.remainder.is_empty()</code> 分支结束执行：</p>
<ol>
<li>正常测试用例 <code>&quot;a b c d e f&quot;</code>，在仅剩下 <code>&quot;f&quot;</code> 时，调用 <code>next()</code>，此时返回 <code>&quot;f&quot;</code>，不再剩余待处理的字符串，但目前的实现，将剩余字符串设置为了空字符串（代码 3，line 30）；</li>
<li>以分隔符结尾的测试用例 <code>&quot;a b c d &quot;</code>，在处理到 <code>&quot;d &quot;</code> 时，调用 <code>next()</code>，此时返回 <code>&quot;d&quot;</code>，剩余待处理字符串为空字符串，需要下一次调用 <code>next()</code> 时返回。</li>
</ol>
<p>目前的实现依赖对空字符串的处理，在进入 <code>self.remainder.is_empty()</code> 分支时，直接返回 <code>None</code>，满足测试用例 1，不满足测试用例 2（二者不能同时满足）。</p>
<p>怎样去除对空字符串的依赖呢？</p>
<ul>
<li>测试用例 1，处理完 <code>&quot;f&quot;</code> 后，没有剩余待处理的字符串 —— 使用 <code>None</code> 表示；</li>
<li>测试用例 2，处理完 <code>&quot;d&quot;</code> 后，还有一个空字符串待处理 —— 使用 <code>Some(&quot;&quot;)</code> 表示。</li>
</ul>
<h2 id="version-3-fix-tail-delimiter"><a class="header" href="#version-3-fix-tail-delimiter">version #3: fix tail delimiter</a></h2>
<p>将 <code>reminder</code> 定义为 <code>Option&lt;&amp;'a str&gt;</code> 类型（代码 4，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3D24b70fb2b35be8dc0896373dd70983d1">待处理字符串定义为 Option</a>）</p>
<ul>
<li><code>Some(&quot;xxx&quot;)</code> —— 仍有待处理的字符串，包括空字符串；</li>
<li><code>None</code> —— 没有剩余待处理的字符串。</li>
</ul>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'a&gt; {
    remainder: Option&lt;&amp;'a str&gt;,
    delimiter: &amp;'a str,
}

impl&lt;'a&gt; StrSplit&lt;'a&gt; {
    pub fn new(haystack: &amp;'a str, delimiter: &amp;'a str) -&gt; Self {
        Self {
            remainder: Some(haystack),
            delimiter,
        }
    }
}

impl&lt;'a&gt; Iterator for StrSplit&lt;'a&gt; {
    type Item = &amp;'a str;

    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
        // &amp;mut &amp;'a str ----------- Option&lt;&amp;'a str&gt;
        // 匹配 self.remainder == Some(&quot;xxx&quot;)，同时获取 val 的可变借用
        if let Some(ref mut remainder) = self.remainder {
            if let Some(next_delim) = remainder.find(self.delimiter) {
                let until_delimiter = &amp;remainder[..next_delim];
                // left without *  - &amp;mut &amp;'a str
                // right - &amp;'a str
                *remainder = &amp;remainder[(next_delim + self.delimiter.len())..];
                Some(until_delimiter)
            } else {
                // https://doc.rust-lang.org/std/option/enum.Option.html#method.take
                // impl&lt;T&gt; Option&lt;T&gt; { fn take(&amp;mut self) -&gt; Option&lt;T&gt; }
                // Takes the value out of the option, leaving a None in its place.
                self.remainder.take()
            }
        } else {
            None
        }
    }
}

#[test]
fn it_works() {
    let haystack = &quot;a b c d e&quot;;
    let letters: Vec&lt;_&gt; = StrSplit::new(haystack, &quot; &quot;).collect();
    assert_eq!(letters, vec![&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;]);
}

#[test]
fn tail_test() {
    let haystack = &quot;a b c d &quot;;
    let letters: Vec&lt;_&gt; = StrSplit::new(haystack, &quot; &quot;).collect();
    assert_eq!(letters, vec![&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;&quot;]);
}
<span class="boring">}
</span></code></pre></pre>
<p>修改后的 <code>next()</code> 实现逻辑如下：</p>
<p>1、首先执行模式匹配，如果仍有待处理的字符串，即 <code>Some(&quot;xxx&quot;)</code>，匹配得到待处理的字符串，记为 <code>remainder</code>；</p>
<p>2、在待处理的字符串中查找分隔符</p>
<ul>
<li>存在分隔符，得到分隔符第一次出现的索引位置，根据索引将字符串分为三个部分，第一部分为此次调用的返回值，第二部分为分隔符，第三部分为下一次调用 <code>next()</code> 时待处理的字符串（即此次调用需要更新待处理的字符串）；</li>
<li>不存在分隔符，直接返回待处理的字符串；并设置剩余待处理字符串为 <code>None</code>（表示没有剩余待处理的字符串），下一次调用 <code>next()</code> 时直接返回 <code>None</code>；</li>
</ul>
<p>3、如果没有待处理的字符串，直接返回 <code>None</code>。</p>
<h3 id="ref-mut"><a class="header" href="#ref-mut">ref mut</a></h3>
<p><code>ref</code> 和 <code>mut</code> 作为 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/reference/patterns.html%23identifier-patterns">Identifier patterns</a> 的关键字：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>IdentifierPattern :
      ref? mut? IDENTIFIER (@ Pattern ) ?
<span class="boring">}
</span></code></pre></pre>
<p>写一段代码测试一下 <code>ref mut</code> 的使用（代码 5，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3Db4587b79cd564092e70d744bfbf34c47">ref mut 测试</a>）</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    let name = String::from(&quot;qiaoin&quot;);
    let gender = String::from(&quot;Male&quot;);
    let mut age = 27;

    age = 28;
    println!(&quot;name = {:?}, gender = {:?}, age = {:?}&quot;, name, gender, age);

    let mut owned_name = name;  // String doesn't impl Copy trait, ownship moved here
    println!(&quot;owned_name = {:?}, gender = {:?}, age = {:?}&quot;, owned_name, gender, age);
    // 可以将注释删除，编译看一下具体的错误信息
    // println!(&quot;name = {:?}, gender = {:?}, age = {:?}&quot;, name, gender, age);  // error, borrowed after move

    // A `ref` borrow on the left side of an assignment is equivalent to
    // an `&amp;` borrow on the right side.
    let ref ref_gender_1 = gender;
    let ref_gender_2 = &amp;gender;
    println!(&quot;owned_name = {:?}, ref_gender_1 = {:?}, age = {:?}&quot;, owned_name, ref_gender_1, age);
    println!(&quot;owned_name = {:?}, ref_gender_2 = {:?}, age = {:?}&quot;, owned_name, ref_gender_2, age);
    println!(&quot;ref_gender_1 equal ref_gender_2 = {}&quot;, *ref_gender_1 == *ref_gender_2);
    println!(&quot;owned_name = {:?}, gender = {:?}, age = {:?}&quot;, owned_name, gender, age);

    // borrowed as mutable
    let ref mut mut_ref_name = owned_name;
    println!(&quot;owned_name = {:?}, gender = {:?}, age = {:?}&quot;, mut_ref_name, gender, age);
    *mut_ref_name = String::from(&quot;桥&quot;);
    println!(&quot;owned_name = {:?}, gender = {:?}, age = {:?}&quot;, mut_ref_name, gender, age);
    // 其后，mut_ref_name 就不是活跃的 mutable borrowed
    // Non-Lexical Lifetimes, NLL for short

    // 因此可以在这里访问 immutable borrowed
    println!(&quot;owned_name = {:?}, gender = {:?}, age = {:?}&quot;, owned_name, gender, age);

    // borrowed as mutable
    let mut_ref_name_2 = &amp;mut owned_name;
    *mut_ref_name_2 = String::from(&quot;qiaoin&quot;);
    println!(&quot;mut_ref_name_2 = {:?}, gender = {:?}, age = {:?}&quot;, mut_ref_name_2, gender, age);
}
</code></pre></pre>
<ul>
<li>在等号左侧使用 <code>ref</code> 不可变借用 === 在等号右侧使用 <code>&amp;</code> 不可变借用</li>
<li>在等号左侧使用 <code>ref mut</code> 可变借用 === 在等号右侧使用 <code>&amp;mut</code> 可变借用</li>
</ul>
<p>既然两者都直接等价，为什么还需要 <code>ref</code> 关键字呢？</p>
<p><code>ref</code> 主要使用在模式匹配（pattern matching）中（<code>let</code> / <code>match</code>），对匹配到的值进行借用（borrow），而不是 Copy 或者 Move 匹配到的值（根据匹配值的类型是否实现了 Copy trait）。</p>
<p>应用于模式匹配语句时，<code>ref</code> 与 <code>&amp;</code> 的比较如下（<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/keyword.ref.html">ref keyword</a>）：</p>
<ul>
<li><code>ref</code> 不作为模式的一部分，不影响值是否匹配，只影响匹配到的值作为借用在 scope 中使用，因此 <code>Foo(ref foo)</code> 和 <code>Foo(foo)</code> 两个模式匹配相同的对象；</li>
<li><code>&amp;</code> 作为模式的一部分，表示待匹配的模式要求为一个对象的引用，因此 <code>&amp;Foo</code> 和 <code>Foo</code> 两个模式匹配不同的对象。</li>
</ul>
<p>假设去掉 <code>ref mut</code>，则后续不能修改。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>if let Some(remainder) = self.remainder {
    // can't mutable ... 
}
<span class="boring">}
</span></code></pre></pre>
<p>假设使用 <code>&amp;mut</code> 进行模式匹配，则右侧类型需要为 <code>Option&lt;&amp;mut T&gt;</code>，匹配后 <code>remainder</code> 的类型为 <code>T</code>，依旧不能修改。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>if let Some(&amp;mut remainder) = self.remainder {
    // can't mutable ...
}
<span class="boring">}
</span></code></pre></pre>
<h3 id="version-31-use--operator"><a class="header" href="#version-31-use--operator">version #3.1 use ? operator</a></h3>
<p><code>next()</code> 实现中有以下的一段代码：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl&lt;'a&gt; Iterator for StrSplit&lt;'a&gt; {
    type Item = &amp;'a str;

    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
        if let Some(ref mut remainder) = self.remainder {
            // do something
        } else {
            None
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<ul>
<li><code>self.remainder</code> 为 <code>Some(val)</code> 时，匹配到内部的 <code>val</code>，得到其可变引用，继续后续的操作；</li>
<li><code>self.remainder</code> 为 <code>None</code> 时，直接返回 <code>None</code>。</li>
</ul>
<p>可以使用 <code>?</code> 操作符实现相同的逻辑。同样写一段代码测试 <code>?</code> 操作符（代码 6，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3D9cab033571d766a16164c7b27d7d5c4c">? operator 测试</a>）</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    if complex_function().is_none() {
        println!(&quot;X not exists!&quot;);
    }
}

fn complex_function() -&gt; Option&lt;&amp;'static str&gt; {
    // 末尾使用 ? operator
    // 如果是 None, 直接返回；如果是 Some(&quot;abc&quot;), set x to &quot;abc&quot;
    let x = get_an_optional_value()?;

    println!(&quot;{}&quot;, x); // &quot;abc&quot; ; if you change line 19 `false` to `true`

    Some(&quot;&quot;)
}

fn get_an_optional_value() -&gt; Option&lt;&amp;'static str&gt; {
    // if the optional value is not empty
    if false {
        return Some(&quot;abc&quot;);
    }

    // else
    None
}
</code></pre></pre>
<p>如何替换 <code>ref mut</code> 的模式匹配呢？本质问题为如何做类型的转换，将类型 <code>&amp;mut Option&lt;&amp;'a str&gt;</code> 转换为类型 <code>Option&lt;&amp;mut &amp;'a str&gt;</code> —— <code>Option::as_mut()</code> 可以完成这个类型转换。因此，简单的优化后得到如下实现（代码 7，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3D083328cfac596d53749413246fa3db2b">version #3.1 use ?</a>）</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl&lt;'a&gt; Iterator for StrSplit&lt;'a&gt; {
    type Item = &amp;'a str;

    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
        // https://doc.rust-lang.org/std/option/enum.Option.html#method.as_mut
        // impl&lt;T&gt; Option&lt;T&gt; { fn as_mut(&amp;mut self) -&gt; Option&lt;&amp;mut T&gt; }
        // self.remainder --- &amp;mut Option&lt;&amp;'a str&gt;
        // self.remainder.as_mut() --- Option&lt;&amp;mut &amp;'a str&gt;
        // self.remainder.as_mut()? --- if Some(&quot;xxx&quot;), type is &amp;mut &amp;'a str
        let remainder = self.remainder.as_mut()?;
        if let Some(next_delim) = remainder.find(self.delimiter) {
            let until_delimiter = &amp;remainder[..next_delim];
            // left without *  - &amp;mut &amp;'a str
            // right - &amp;'a str
            *remainder = &amp;remainder[(next_delim + self.delimiter.len())..];
            Some(until_delimiter)
        } else {
            // https://doc.rust-lang.org/std/option/enum.Option.html#method.take
            // impl&lt;T&gt; Option&lt;T&gt; { fn take(&amp;mut self) -&gt; Option&lt;T&gt; }
            self.remainder.take()
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<h2 id="version-4-multiple-lifetimes"><a class="header" href="#version-4-multiple-lifetimes">version #4: multiple lifetimes</a></h2>
<h3 id="already-done"><a class="header" href="#already-done">already done？</a></h3>
<p>思考一个问题，<code>remainder</code> 和 <code>delimiter</code> 需要为相同的生命周期吗？</p>
<p>来看下面一个例子，现在有一个函数使用 <code>StrSplit</code> 提供的字符串分割能力，其对外 API 使用 <code>char</code> 作为分隔符，因此在调用 <code>StrSplit</code> 前需通过 <code>char</code> 得到 <code>&amp;str</code>（代码 8，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3D5b529c238e753c03465a6ef7982068ed">使用 char 作为分隔符</a>）</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn until_char(s: &amp;str, c: char) -&gt; &amp;str {
    let delim = format!(&quot;{}&quot;, c);
    StrSplit::new(s, &amp;delim)
        .next()
        .expect(&quot;StrSplit should have at least one result&quot;)
}

#[test]
fn test_until_char() {
    assert_eq!(until_char(&quot;hello, world&quot;, 'r'), &quot;hello, wo&quot;);
}
<span class="boring">}
</span></code></pre></pre>
<p>执行编译，报错信息如下：</p>
<pre><code class="language-bash">Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing local variable `delim`
  --&gt; src/lib.rs:41:5
   |
41 |       StrSplit::new(s, &amp;delim)
   |       ^                ------ `delim` is borrowed here
   |  _____|
   | |
42 | |         .next()
43 | |         .expect(&quot;StrSplit should have at least one result&quot;)
   | |___________________________________________________________^ returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.
</code></pre>
<p>同样，可以查看 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/stable/error-index.html%23E0515">E0515</a> 获取更多信息，但这里的解决方案需要从根本上去挖掘。</p>
<p>让我们回到本小节开头的那个问题，<code>remainder</code> 和 <code>delimiter</code> 是否需要为相同的生命周期？<code>StrSplit</code> 字符串分割应该与待处理字符串 <code>remainder</code> 的生命周期保持一致，与分隔符 <code>delimiter</code> 的生命周期没有直接联系。</p>
<p>但在目前的 <code>struct StrSplit</code> 定义中，只声明了一个生命周期参数 <code>'a</code>，<code>remainder</code> 和 <code>delimiter</code> 有相同的生命周期标注。同时，在实现 Iterator trait 时，返回值与 <code>remainder</code> 的生命周期保持一致，也是 <code>'a</code>。</p>
<p>在 <code>until_char</code> 进行编译时，传递给 <code>StrSplit::new(s, &amp;delim)</code> 的两个参数拥有不同的生命周期：</p>
<ul>
<li><code>s</code> 的生命周期 &gt;= <code>delim</code> 的生命周期；</li>
<li><code>delim</code> 的生命周期为当前函数体；在执行完函数后，就会被 Drop 掉。</li>
</ul>
<p>由于我们在 <code>struct StrSplit</code> 定义中将两个参数标注为相同的生命周期，此时，编译器认为 <code>s</code> 和临时变量 <code>delim</code> 应该拥有相同的生命周期，因此会将长的生命周期（longer lifetime）转化为短的生命周期（shorter lifetime）。在 <code>until_char</code> 返回时，返回的引用的生命周期与 <code>delim</code> 临时变量的生命周期相绑定（也即与函数 <code>until_char</code> 的生命周期相绑定），而临时变量的生命周期会在函数执行完毕后被 Drop 掉，因此编译器给到报错。</p>
<p>真实情况是，我们需要的 <code>until_char</code> 函数签名如下：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn until_char&lt;'s&gt;(s: &amp;'s str, c: char) -&gt; &amp;'s str {}
//                     ^                    ^
//                     |                    |
//          待处理的字符串引用的生命周期    返回的引用的生命周期
<span class="boring">}
</span></code></pre></pre>
<p>我们希望返回的引用的生命周期与待处理的字符串引用的生命周期相绑定。</p>
<h3 id="add-multiple-lifetime"><a class="header" href="#add-multiple-lifetime">add multiple lifetime</a></h3>
<p><code>struct StrSplit</code> 定义的两个参数，使用不同的生命周期参数进行标注（代码 9，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3D646edaa857f6298a7c4b38dc507a5be3">使用多个生命周期参数标注</a>）</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'haystack, 'delimiter&gt; {
    remainder: Option&lt;&amp;'haystack str&gt;,
    delimiter: &amp;'delimiter str,
}

impl&lt;'haystack, 'delimiter&gt; StrSplit&lt;'haystack, 'delimiter&gt; {
    pub fn new(haystack: &amp;'haystack str, delimiter: &amp;'delimiter str) -&gt; Self {
        Self {
            remainder: Some(haystack),
            delimiter,
        }
    }
}

impl&lt;'haystack, 'delimiter&gt; Iterator for StrSplit&lt;'haystack, 'delimiter&gt; {
    type Item = &amp;'haystack str;

    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
        let remainder = self.remainder.as_mut()?;
        if let Some(next_delim) = remainder.find(self.delimiter) {
            let until_delimiter = &amp;remainder[..next_delim];
            *remainder = &amp;remainder[(next_delim + self.delimiter.len())..];
            Some(until_delimiter)
        } else {
            self.remainder.take()
        }
    }
}

fn until_char(s: &amp;str, c: char) -&gt; &amp;str {
    let delim = format!(&quot;{}&quot;, c);  // 每次构造 delimiter 都需要进行一次堆上的内存分配
    StrSplit::new(s, &amp;delim)
        .next()
        .expect(&quot;StrSplit should have at least one result&quot;)
}

#[test]
fn test_until_char() {
    assert_eq!(until_char(&quot;hello, world&quot;, 'r'), &quot;hello, wo&quot;);
}
<span class="boring">}
</span></code></pre></pre>
<p>至此，我们就正确实现了字符串分割的功能。在目前的实现中，<code>delimiter</code> 是一个 <code>&amp;str</code> 类型的分隔符；我们期望更通用一点（anything can find itself in a str）。</p>
<h2 id="version-5-generic-delimiter"><a class="header" href="#version-5-generic-delimiter">version #5: generic delimiter</a></h2>
<p>明确一下目标：按照分隔符对目标字符串进行分割</p>
<ul>
<li>操作的对象 —— 字符串；</li>
<li>分割字符串 —— 根据分隔符将目标字符串分割为三个部分；</li>
</ul>
<pre><code class="language-bash">xxxxxxxxxxxxxxx1xxxxxxx3xxx4xxxxx6xxxx8x
     first part     ^     third part
                    |
                second part
</code></pre>
<ul>
<li>索引值 —— 至少需要两个索引值将字符串分割为三个部分，1）分隔符的开始索引，2）分隔符的结束索引+1（为了方便处理，类似编程语言中的 <code>end()</code> 指向最后一个元素的下一个位置）；如果分隔符长度固定，可以只需要一个索引值，但这里考虑分隔符可能为正则表达式，会匹配不同长度的分隔符，因此确定为两个索引值。</li>
</ul>
<h3 id="使用-trait-定义分隔符"><a class="header" href="#使用-trait-定义分隔符">使用 trait 定义分隔符</a></h3>
<blockquote>
<p>how to use <em>traits</em> to define behavior in a generic way</p>
</blockquote>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub trait Delimiter {
    // 在字符串中查找分隔符 self
    // 1）找到，返回 (分隔符的开始索引, 分隔符的结束索引+1)
    // 2）未找到，返回 None
    fn find_next(&amp;self, s: &amp;str) -&gt; Option&lt;(usize, usize)&gt;;
}
<span class="boring">}
</span></code></pre></pre>
<p><code>StrSplit</code> 的第二个参数，实现 Delimiter trait：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'haystack, D&gt; {
    remainder: Option&lt;&amp;'haystack str&gt;,
    delimiter: D,
}

impl&lt;'haystack, D&gt; StrSplit&lt;'haystack, D&gt; {
    pub fn new(haystack: &amp;'haystack str, delimiter: D) -&gt; Self {
        Self {
            remainder: Some(haystack),
            delimiter,
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<h3 id="为不同的分隔符类型实现-delimiter-trait"><a class="header" href="#为不同的分隔符类型实现-delimiter-trait">为不同的分隔符类型实现 Delimiter trait</a></h3>
<ul>
<li><code>&amp;str</code> 实现 Delimiter trait</li>
</ul>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl Delimiter for &amp;str {
    fn find_next(&amp;self, s: &amp;str) -&gt; Option&lt;(usize, usize)&gt; {
        s.find(self).map(|start| (start, start + self.len()))
    }
}
<span class="boring">}
</span></code></pre></pre>
<ul>
<li><code>char</code> 实现 Delimiter trait</li>
</ul>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl Delimiter for char {
    fn find_next(&amp;self, s: &amp;str) -&gt; Option&lt;(usize, usize)&gt; {
        s.char_indices()
            .find(|(_, c)| c == self)
            .map(|(start, _)| (start, start + self.len_utf8()))
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>更多其他类型都可以按需实现。</p>
<h3 id="strfind--optionmap"><a class="header" href="#strfind--optionmap">str::find &amp; Option::map</a></h3>
<p>在 <code>&amp;str</code> 实现 Delimiter trait 时，<code>s.find(self)</code> 传入的是一个字符串；而在 <code>&amp;str</code> 实现 Delimiter trait 时，<code>s.char_indices().find(|(_, c)| c == self)</code> 传入的是一个闭包（closure）。看一下 <code>str::find()</code> 的函数签名：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn find&lt;'a, P&gt;(&amp;'a self, pat: P) -&gt; Option&lt;usize&gt;
where
    P: Pattern&lt;'a&gt;,
<span class="boring">}
</span></code></pre></pre>
<p>实现功能为，在字符串中搜索匹配的 Pattern，返回匹配到的字符串的开始索引 <code>Some(usize)</code>；未找到，返回 <code>None</code>。对于 Pattern trait 的讨论我们放在本文的最后一节。</p>
<p>结合 <code>Option::map</code> 对匹配的结果做一下转换：</p>
<ul>
<li><code>Some(usize)</code> —— 在匹配的分隔符开始索引上，apply 闭包，得到 <code>Some(分隔符开始索引, 分隔符结束索引+1)</code></li>
<li><code>None</code> —— 返回 <code>None</code></li>
</ul>
<h3 id="最终代码实现"><a class="header" href="#最终代码实现">最终代码实现</a></h3>
<p>完整的代码如下（代码 10，<a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/%3Fversion%3Dstable%26mode%3Ddebug%26edition%3D2021%26gist%3D6dbcb7c0a283387d922d189b683b4a1d">最终实现</a>）：</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct StrSplit&lt;'haystack, D&gt; {
    remainder: Option&lt;&amp;'haystack str&gt;,
    delimiter: D,
}

impl&lt;'haystack, D&gt; StrSplit&lt;'haystack, D&gt; {
    pub fn new(haystack: &amp;'haystack str, delimiter: D) -&gt; Self {
        Self {
            remainder: Some(haystack),
            delimiter,
        }
    }
}

pub trait Delimiter {
    // 在字符串中查找分隔符 self
    // 1）找到，返回 (分隔符的开始索引, 分隔符的结束索引+1)
    // 2）未找到，返回 None
    fn find_next(&amp;self, s: &amp;str) -&gt; Option&lt;(usize, usize)&gt;;
}

impl Delimiter for &amp;str {
    fn find_next(&amp;self, s: &amp;str) -&gt; Option&lt;(usize, usize)&gt; {
        s.find(self).map(|start| (start, start + self.len()))
    }
}

impl Delimiter for char {
    fn find_next(&amp;self, s: &amp;str) -&gt; Option&lt;(usize, usize)&gt; {
        s.char_indices()
            .find(|(_, c)| c == self)
            .map(|(start, _)| (start, start + self.len_utf8()))
    }
}

impl&lt;'haystack, D&gt; Iterator for StrSplit&lt;'haystack, D&gt;
where
    D: Delimiter,
{
    type Item = &amp;'haystack str;

    fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
        // https://doc.rust-lang.org/std/option/enum.Option.html#method.as_mut
        // impl&lt;T&gt; Option&lt;T&gt; { fn as_mut(&amp;mut self) -&gt; Option&lt;&amp;mut T&gt; }
        // self.remainder --- &amp;mut Option&lt;&amp;'a str&gt;
        // self.remainder.as_mut() --- Option&lt;&amp;mut &amp;'a str&gt;
        // self.remainder.as_mut()? --- if Some(&quot;xxx&quot;), type is &amp;mut &amp;'a str
        let remainder = self.remainder.as_mut()?;
        if let Some((delim_start, delim_end)) = self.delimiter.find_next(remainder) {
            let until_delimiter = &amp;remainder[..delim_start];
            // left without *  - &amp;mut &amp;'a str
            // right - &amp;'a str
            *remainder = &amp;remainder[delim_end..];
            Some(until_delimiter)
        } else {
            // https://doc.rust-lang.org/std/option/enum.Option.html#method.take
            // impl&lt;T&gt; Option&lt;T&gt; { fn take(&amp;mut self) -&gt; Option&lt;T&gt; }
            self.remainder.take()
        }
    }
}

fn until_char(s: &amp;str, c: char) -&gt; &amp;str {
    StrSplit::new(s, c)
        .next()
        .expect(&quot;StrSplit should have at least one result&quot;)
}

#[test]
fn test_until_char() {
    assert_eq!(until_char(&quot;hello, world&quot;, 'r'), &quot;hello, wo&quot;);
}

#[test]
fn it_works() {
    let haystack = &quot;a b c d e&quot;;
    let letters: Vec&lt;_&gt; = StrSplit::new(haystack, &quot; &quot;).collect();
    assert_eq!(letters, vec![&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;]);
}

#[test]
fn tail_test() {
    let haystack = &quot;a b c d &quot;;
    let letters: Vec&lt;_&gt; = StrSplit::new(haystack, &quot; &quot;).collect();
    assert_eq!(letters, vec![&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;&quot;]);
}
<span class="boring">}
</span></code></pre></pre>
<p>至此，我们实现了 <code>StrSplit</code>，支持自定义 <code>Delimiter</code>（对应类型实现 Delimiter trait）</p>
<h2 id="标准库-strsplit"><a class="header" href="#标准库-strsplit">标准库 str::split</a></h2>
<p>标准库的 <code>str::split</code> 实现，<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/primitive.str.html%23method.split">str - split</a></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn split&lt;'a, P&gt;(&amp;'a self, pat: P) -&gt; Split&lt;'a, P&gt;
where
    P: Pattern&lt;'a&gt;,
<span class="boring">}
</span></code></pre></pre>
<h3 id="pat-为-str-类型时split-完整的调用链路"><a class="header" href="#pat-为-str-类型时split-完整的调用链路">pat 为 &amp;str 类型时，split() 完整的调用链路</a></h3>
<p>测试代码</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
    let mut a = &quot;hello world&quot;.split(&quot; &quot;);
    let b = a.next();
    println!(&quot;{:?}&quot;, b);  // Some(&quot;hello&quot;)
}
</code></pre></pre>
<p>1、<code>&quot;hello world&quot;.split(&quot; &quot;)</code> 调用 <code>str::split()</code> 返回 <code>Split struct</code></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// https://doc.rust-lang.org/src/core/str/mod.rs.html#1217
pub fn split&lt;'a, P: Pattern&lt;'a&gt;&gt;(&amp;'a self, pat: P) -&gt; Split&lt;'a, P&gt; {
    Split(SplitInternal {
        start: 0,
        end: self.len(),
        matcher: pat.into_searcher(self),  // StrSearcher
        allow_trailing_empty: true,
        finished: false,
    })
}
<span class="boring">}
</span></code></pre></pre>
<p>2、<code>a.next()</code> 返回匹配的字符串，查看 <code>Split struct</code> 实现的 Iterator trait <code>next()</code>，</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// 宏定义 https://doc.rust-lang.org/src/core/str/iter.rs.html#450
// Split struct 定义 https://doc.rust-lang.org/src/core/str/iter.rs.html#733
impl&lt;'a, P&gt; Iterator for Split&lt;'a, P&gt;
where
    P: Pattern&lt;'a&gt;,
{
    type Item = &amp;'a str;

    pub fn next(&amp;mut self) -&gt; Option&lt;&amp;'a str&gt; {
        // self.0 ---- core::str::iter::SplitInternal&lt;'_, &amp;str&gt;
        self.0.next()
    }
}
<span class="boring">}
</span></code></pre></pre>
<p>3、调用 <code>SplitInternal::next()</code></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// https://doc.rust-lang.org/src/core/str/iter.rs.html#599
impl&lt;'a, P: Pattern&lt;'a&gt;&gt; SplitInternal&lt;'a, P&gt; {
    fn next(&amp;mut self) -&gt; Option&lt;&amp;'a str&gt; {
        if self.finished {
            return None;
        }

        let haystack = self.matcher.haystack();
        match self.matcher.next_match() {
            // SAFETY: `Searcher` guarantees that `a` and `b` lie on unicode boundaries.
            Some((a, b)) =&gt; unsafe {
                let elt = haystack.get_unchecked(self.start..a);
                self.start = b;
                Some(elt)
            },
            None =&gt; self.get_end(),  // 将 self.finished 设置为 true，下一次调用返回 None
        }
    }
}
<span class="boring">}
</span></code></pre></pre>
<ul>
<li>步骤 1 中，调用 <code>split()</code> 构造 <code>Split struct</code> 返回，其中 <code>matcher: pat.into_searcher(self)</code>，通过 <code>Pattern::into_searcher</code> 作为构造器去构造出一个 <code>StrSearcher</code></li>
</ul>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// https://doc.rust-lang.org/src/core/str/pattern.rs.html#91-155
pub trait Pattern&lt;'a&gt; {
    type Searcher: Searcher&lt;'a&gt;;
    fn into_searcher(self, haystack: &amp;'a str) -&gt; Self::Searcher;
}

// https://doc.rust-lang.org/src/core/str/pattern.rs.html#862-904
impl&lt;'a, 'b&gt; Pattern&lt;'a&gt; for &amp;'b str {
    type Searcher = StrSearcher&lt;'a, 'b&gt;;

    fn into_searcher(self, haystack: &amp;'a str) -&gt; StrSearcher&lt;'a, 'b&gt; {
        StrSearcher::new(haystack, self)  // haystack --- &quot;hello world&quot;; self -- &quot; &quot;
    }
}
<span class="boring">}
</span></code></pre></pre>
<ul>
<li><code>self.matcher.haystack()</code> 获取得到待处理的字符串</li>
<li><code>self.matcher.next_match()</code> 获取匹配到 <code>&quot;&quot;</code> 的起始索引（详细实现就不贴了，有兴趣的同学可以查看 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/str/pattern/trait.Searcher.html%23method.next_match">Search trait 的文档说明</a>） —— <code>(start_match, end_match)</code>，其中 <code>start_match</code> 表示 Pattern 的开始索引，<code>end_match</code> 表示 Pattern 的结束索引+1。</li>
<li><code>Some((a, b))</code> 匹配后，将匹配到的字符串返回，同时修改待处理字符串的 <code>start</code> 索引</li>
</ul>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub unsafe trait Searcher&lt;'a&gt; {
    // Required methods
    fn haystack(&amp;self) -&gt; &amp;'a str;
    fn next(&amp;mut self) -&gt; SearchStep;

    // Provided methods
    fn next_match(&amp;mut self) -&gt; Option&lt;(usize, usize)&gt; { ... }
    fn next_reject(&amp;mut self) -&gt; Option&lt;(usize, usize)&gt; { ... }
}

// https://doc.rust-lang.org/src/core/str/pattern.rs.html#962-1050
unsafe impl&lt;'a, 'b&gt; Searcher&lt;'a&gt; for StrSearcher&lt;'a, 'b&gt; {
    fn haystack(&amp;self) -&gt; &amp;'a str {
        self.haystack  // first call, return &quot;hello world&quot;
    }

    fn next(&amp;mut self) -&gt; SearchStep { ... }

    fn next_match(&amp;mut self) -&gt; Option&lt;(usize, usize)&gt; { ... }
}
<span class="boring">}
</span></code></pre></pre>
<p>主要的逻辑都是围绕一个数据结构 <code>Split struct</code> 和两个 trait（Pattern trait 和 Search trait）进行的，我们分别看一下。</p>
<h3 id="split-struct"><a class="header" href="#split-struct">Split struct</a></h3>
<p><code>Split struct</code> 使用 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/src/core/str/iter.rs.html%23728-744">宏进行实现</a>，<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/src/core/str/iter.rs.html%23450">宏定义</a> 中实现了 Iterator trait（同时还实现了 DoubleEndedIterator 和 FusedIterator，这里暂不讨论），因此测试代码中可以 <code>a.next()</code> 进行调用。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Split&lt;'a, P&gt;(_)
 where
    P: Pattern&lt;'a&gt;;

impl&lt;'a, P: Pattern&lt;'a&gt;&gt; Split&lt;'a, P&gt; {
    /// Returns remainder of the splitted string 返回剩余待处理的字符串
    pub fn as_str(&amp;self) -&gt; &amp;'a str {
        self.0.as_str()
    }
}

impl&lt;'a, P&gt; Iterator for Split&lt;'a, P&gt;
where
    P: Pattern&lt;'a&gt;,
{
    type Item = &amp;'a str;

    pub fn next(&amp;mut self) -&gt; Option&lt;&amp;'a str&gt; {
        self.0.next()
    }
}
<span class="boring">}
</span></code></pre></pre>
<h3 id="pattern-trait"><a class="header" href="#pattern-trait">Pattern trait</a></h3>
<p><a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/str/pattern/trait.Pattern.html">Pattern trait</a>（类似我们定义的 Delimiter trait，但 Pattern trait 要复杂很多）包含一个关联类型 <code>type Searcher</code>，<code>into_searcher</code> 作为构造器去构造出特定类型的 <code>Searcher</code>（作为真实的执行者，进行字符串匹配操作）。</p>
<p>实现了 Pattern trait 的六种类型都可以作为 <code>split()</code> 的参数，在 <code>haystack: &amp;'a str</code> 中搜索匹配的字符串，<a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/str/pattern/trait.Pattern.html">表格 1</a> 展示了对应的类型和搜索匹配之间的关系。</p>
<div class="table-wrapper"><table><thead><tr><th>Pattern type</th><th>Match condition</th></tr></thead><tbody>
<tr><td>&amp;str</td><td>is substring</td></tr>
<tr><td>char</td><td>is contained in string</td></tr>
<tr><td>&amp;[char]</td><td>any char in slice is contained in string</td></tr>
<tr><td>F: FnMut(char) -&gt; bool</td><td>F returns true for a char in string</td></tr>
<tr><td>&amp;&amp;str</td><td>is substring</td></tr>
<tr><td>&amp;String</td><td>is substring</td></tr>
</tbody></table>
</div>
<p>表格 1：实现 Pattern trait 的六种类型与搜索匹配的对应关系</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub trait Pattern&lt;'a&gt; {
    type Searcher: Searcher&lt;'a&gt;;
    fn into_searcher(self, haystack: &amp;'a str) -&gt; Self::Searcher;
}

// 以下六个 structs 实现了 Pattern trait
impl&lt;'a&gt; Pattern&lt;'a&gt; for char { ... }

impl&lt;'a, 'b&gt; Pattern&lt;'a&gt; for &amp;'b str {
    type Searcher = StrSearcher&lt;'a, 'b&gt;;
}

impl&lt;'a, 'b&gt; Pattern&lt;'a&gt; for &amp;'b String { ... }

impl&lt;'a, 'b&gt; Pattern&lt;'a&gt; for &amp;'b [char] { ... }

impl&lt;'a, 'b, 'c&gt; Pattern&lt;'a&gt; for &amp;'c &amp;'b str { ... }

impl&lt;'a, F&gt; Pattern&lt;'a&gt; for F
where
    F: FnMut(char) -&gt; bool, 
{ ... }
<span class="boring">}
</span></code></pre></pre>
<h3 id="search-trait"><a class="header" href="#search-trait">Search trait</a></h3>
<p>真实地进行字符串匹配的执行者，从给定字符串的起点位置（字符串最左侧）开始匹配对应的 Pattern。需要注意的是，Search trait 被标记为 <code>unsafe</code>，原因是 <code>next()</code> 返回的索引值需要保证正好落在有效的 UTF-8 边界上（lie on valid utf8 boundaries in the haystack），详细说明可以查看 <a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/std/str/pattern/trait.Searcher.html">文档</a></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub unsafe trait Searcher&lt;'a&gt; {
    // Required methods
    fn haystack(&amp;self) -&gt; &amp;'a str;
    fn next(&amp;mut self) -&gt; SearchStep;

    // Provided methods
    fn next_match(&amp;mut self) -&gt; Option&lt;(usize, usize)&gt; { ... }
    fn next_reject(&amp;mut self) -&gt; Option&lt;(usize, usize)&gt; { ... }
}

// 以下四个 structs 实现了 Search trait
unsafe impl&lt;'a&gt; Searcher&lt;'a&gt; for CharSearcher&lt;'a&gt; { ... }

unsafe impl&lt;'a, 'b&gt; Searcher&lt;'a&gt; for CharSliceSearcher&lt;'a, 'b&gt; { ... }

unsafe impl&lt;'a, 'b&gt; Searcher&lt;'a&gt; for StrSearcher&lt;'a, 'b&gt; { ... }

unsafe impl&lt;'a, F&gt; Searcher&lt;'a&gt; for CharPredicateSearcher&lt;'a, F&gt;
where
    F: FnMut(char) -&gt; bool,
{ ... }
<span class="boring">}
</span></code></pre></pre>
<h2 id="总结"><a class="header" href="#总结">总结</a></h2>
<p>在本文中，我们围绕着字符串分割的实例，详细讲解了 Rust 中生命周期，包括为什么需要生命周期、什么是生命周期、以及如何标注生命周期。同时，针对字符串分割仅与待处理字符串的生命周期相关联，我们引入了多生命周期。最后，使用 trait 来定义分割行为，让实现更加通用。</p>
<p>通过 5 个版本的修改，一步步完成我们自己的 StrSplit，最后查看标准库的字符串分割实现，加深理解。</p>
<p>除了对生命周期相关概念的讲解外，本文还对实现中的一些细节做了讲解：</p>
<ul>
<li><code>&amp;str</code> 与 <code>String</code> 的区别与联系</li>
<li><code>Iterator trait</code></li>
<li><code>Self</code> 和 <code>self</code></li>
<li><code>ref mut</code> 进行模式匹配</li>
<li><code>?</code> operator</li>
<li>etc ...</li>
</ul>
<p>本文为作者刚开始学习 Rust 的一篇学习笔记，如果遗漏或错误，欢迎大家在评论区讨论指出。</p>
<h2 id="references"><a class="header" href="#references">References</a></h2>
<ul>
<li>Crust of Rust 系列 <a href="https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DrAl-9HwD858%26list%3DPLqbS7AVVErFiWDOAVrPt7aYmnuuOLYvOa%26index%3D1%26ab_channel%3DJonGjengset">Lifetime Annotations</a> 本文为学习此视频后的笔记</li>
<li><a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/stable/book/">Rust The Book</a> 第 4 章和第 10 章</li>
<li>极客时间专栏 <a href="https://link.zhihu.com/?target=https%3A//time.geekbang.org/column/intro/100085301">陈天 · Rust 编程第一课</a>，第 7 讲 - 第 11 讲</li>
<li><a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/stable/std/">Rust 标准库文档</a>，对于 Rust 的源码直接从 docs.rs 点击过去看一下</li>
<li><a href="https://link.zhihu.com/?target=https%3A//cheats.rs/">Rust 语言的备忘清单</a></li>
<li><a href="https://link.zhihu.com/?target=https%3A//stackoverflow.com/questions/57754901/what-is-a-fat-pointer">rust - What is a &quot;fat pointer&quot;? - Stack Overflow</a></li>
<li><a href="https://link.zhihu.com/?target=https%3A//www.reddit.com/r/rust/comments/kcou9c/authors_of_programming_rust_2nd_edition_have_a/">Authors of &quot;Programming Rust 2nd Edition&quot; have a sense of humor : rust</a> 文中 <code>noodles</code> 的代码示例和图示受这个帖子启发，有删改</li>
<li><a href="https://link.zhihu.com/?target=https%3A//play.rust-lang.org/">Rust Playground</a> 文中的代码示例都给到了 playground 的链接，在阅读的时候可以点击跳转过去 Run 起来看一下运行结果或错误提示</li>
<li><a href="https://link.zhihu.com/?target=https%3A//doc.rust-lang.org/stable/error-index.html">Rust Compiler Error Index</a> Rust 错误列表，在 playground 中运行报错时可以直接点击跳转过来查看，作为字典查询即可</li>
</ul>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../../empty.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>

                            <a rel="next" href="../../02_生命周期/与ChatGPT深度对话来学Rust生命周期/与ChatGPT深度对话来学Rust生命周期.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>

                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../../empty.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>

                    <a rel="next" href="../../02_生命周期/与ChatGPT深度对话来学Rust生命周期/与ChatGPT深度对话来学Rust生命周期.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>




        <script type="text/javascript">
            window.playground_copyable = true;
        </script>


        <script src="../../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../searcher.js" type="text/javascript" charset="utf-8"></script>

        <script src="../../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->


    </body>
</html>
